package org.mozhu.mboot.core.security;

import com.google.common.collect.Maps;
import org.mozhu.mboot.core.util.MessageUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;

public class Permissions {

    public static final String CREATE_PERMISSION = "create";
    public static final String UPDATE_PERMISSION = "update";
    public static final String DELETE_PERMISSION = "delete";
    public static final String READ_PERMISSION = "read";

    /**
     * 资源前缀
     */
    private String resourceIdentity;

    /**
     * key: 权限 value : 资源
     */
    private Map<String, String> resourcePermissions = Maps.newConcurrentMap();

    /**
     * 自动生成 create update delete 的权限串
     *
     * @param resourceIdentity
     * @return
     */
    public static Permissions newPermissions(String resourceIdentity) {

        Permissions permissions = new Permissions();

        permissions.resourceIdentity = resourceIdentity;

        permissions.resourcePermissions.put(CREATE_PERMISSION, resourceIdentity + ":" + CREATE_PERMISSION);
        permissions.resourcePermissions.put(UPDATE_PERMISSION, resourceIdentity + ":" + UPDATE_PERMISSION);
        permissions.resourcePermissions.put(DELETE_PERMISSION, resourceIdentity + ":" + DELETE_PERMISSION);
        permissions.resourcePermissions.put(READ_PERMISSION, resourceIdentity + ":" + READ_PERMISSION);

        return permissions;
    }

    /**
     * 添加权限 自动生成如showcase:sample:permission
     *
     * @param permission
     */
    public void addPermission(String permission) {
        resourcePermissions.put(permission, resourceIdentity + ":" + permission);
    }

    public void assertHasCreatePermission() {
        assertHasPermission(CREATE_PERMISSION, "no.create.permission");
    }

    public void assertHasUpdatePermission() {
        assertHasPermission(UPDATE_PERMISSION, "no.update.permission");
    }

    public void assertHasDeletePermission() {
        assertHasPermission(DELETE_PERMISSION, "no.delete.permission");
    }

    public void assertHasReadPermission() {
        assertHasPermission(READ_PERMISSION, "no.read.permission");
    }

    /**
     * 即增删改中的任何一个即可
     */
    public void assertHasEditPermission() {
        assertHasAnyPermission(new String[]{CREATE_PERMISSION, UPDATE_PERMISSION, DELETE_PERMISSION});
    }

    public void assertHasPermission(String permission) {
        assertHasPermission(permission, null);
    }

    public void assertHasPermission(String permission, String errorCode) {
        if (StringUtils.isEmpty(errorCode)) {
            errorCode = getDefaultErrorCode();
        }
        String resourcePermission = getOrCreateResourcePermission(permission);
        if (!hasPermission(resourcePermission)) {
            throw new AccessDeniedException(MessageUtils.message(errorCode, resourcePermission));
        }
    }

    private boolean hasPermission(String resourcePermission) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return false;
        }
        Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
        return PermissionUtils.hasPermission(authorities, resourcePermission);
    }

    public void assertHasAllPermission(String[] permissions) {
        assertHasAllPermission(permissions, null);
    }

    public void assertHasAllPermission(String[] permissions, String errorCode) {
        if (StringUtils.isEmpty(errorCode)) {
            errorCode = getDefaultErrorCode();
        }

        if (permissions == null || permissions.length == 0) {
            throw new AccessDeniedException(message(permissions, errorCode));
        }

        for (String permission : permissions) {
            String resourcePermission = getOrCreateResourcePermission(permission);
            if (!hasPermission(resourcePermission)) {
                throw new AccessDeniedException(message(permissions, errorCode));
            }
        }
    }

    private String message(String[] permissions, String errorCode) {
        return MessageUtils.message(errorCode, resourceIdentity + ":" + Arrays.toString(permissions));
    }

    private String getOrCreateResourcePermission(String permission) {
        String resourcePermission = resourcePermissions.get(permission);
        if (resourcePermission == null) {
            resourcePermission = this.resourceIdentity + ":" + permission;
            resourcePermissions.put(permission, resourcePermission);
        }
        return resourcePermission;
    }

    public void assertHasAnyPermission(String[] permissions) {
        assertHasAnyPermission(permissions, null);
    }

    public void assertHasAnyPermission(String[] permissions, String errorCode) {
        if (StringUtils.isEmpty(errorCode)) {
            errorCode = getDefaultErrorCode();
        }
        if (permissions == null || permissions.length == 0) {
            throw new AccessDeniedException(message(permissions, errorCode));
        }

        for (String permission : permissions) {
            String resourcePermission = getOrCreateResourcePermission(permission);
            if (hasPermission(resourcePermission)) {
                return;
            }
        }

        throw new AccessDeniedException(message(permissions, errorCode));
    }

    private String getDefaultErrorCode() {
        return "no.permission";
    }

    public String getResourceIdentity() {
        return resourceIdentity;
    }

}
